#include<linux/module.h>
#include <linux/console.h>

/*声明printk()这个内核态的函数*/
#include<linux/kernel.h>

/*文件系统有关的，结构体file_operations也在fs头文件定义*/
#include<linux/fs.h>

/*init和exit相关宏*/
#include<linux/init.h>
#include<linux/delay.h>
#include<linux/poll.h>

/*linux中断定义*/
#include<linux/irq.h>

/**/
#include<asm/irq.h>

/*包含与中断相关的大部分宏及结构体的定义，request_irq()等*/
#include<linux/interrupt.h>

/*linux中的用户态内存交互函数，copy_from_user(),copy_to_user()等*/
#include<asm/uaccess.h>
//#include<mach/regs-gpio.h>
//#include<mach/hardware.h>
#include<linux/platform_device.h>
#include<linux/cdev.h>

/*misc混合设备注册与注销*/
#include<linux/miscdevice.h>
#include <asm/io.h>
#include <linux/io.h>
#include <linux/of_gpio.h>

/*时间获取*/
#include <linux/time.h>
#include <linux/timer.h> 
#include <linux/rtc.h>
#include <linux/timex.h>
#include <linux/list.h>

#include <linux/gpio.h>
#include <linux/of_gpio.h>

#include "flymap_vsync.h"

#define REG_WRITE(addr,value)  ((*(volatile uintptr_t *)(addr)) = (value))
#define REG_READ(Addr)         (*(volatile uintptr_t *)(Addr))

#define GPIO5_BASE_ADDR 0x011095000
#define GPIO5_REG_ADDR  0x011095000
#define GPIO5_NUM(a) 5*8+a
#define GPIO_5_1    1
#define GPIO_BASE_ADDR  GPIO5_BASE_ADDR
#define GPIO_REG_ADDR   GPIO5_REG_ADDR
#define GPIO_NUM        GPIO5_NUM(GPIO_5_1)

#define GPIO4_BASE_ADDR 0x011094000
#define GPIO4_REG_ADDR  0x011094000
#define GPIO4_NUM(a) 4*8+a

#define GPIO6_BASE_ADDR 0x011096000
#define GPIO6_REG_ADDR  0x011096000
#define GPIO6_NUM(a) 6*8+a

#define GPIO11_BASE_ADDR 0x01109B000
#define GPIO11_REG_ADDR  0x01109B000
#define GPIO11_NUM(a) 11*8+a
#define GPIO_11_5    5

#define GPIO_DIR_ADDR   0x400
#define GPIO_IS_ADDR    0x404
#define GPIO_IEV_ADDR   0x40c
#define GPIO_IE_ADDR   0x410
#define GPIO_IC_ADDR    0x41c

#define MAX_TRIG_NUM    10


static uintptr_t reg_11_virtual_addr = 0;
/*数组中是否有数据标志，0表示无数据可读，1表示有数字可读*/
static volatile char start_flag = 0;

struct trig_time_touser {
    uint32_t u32seq;
    uint64_t u64TmMoni;
    uint64_t u64TmSys;
};

struct trig_time_rec {
    uint32_t u32seq;
    uint64_t u64TmMoni;
    uint64_t u64TmSys;
    struct list_head stNode;
};

struct trig_record {
    uint32_t u32Seq;
    uint32_t u32CurNum;
    struct list_head stList;
};

/* 高精度定时器 */
static struct hrtimer g_hrtimer_io_trigger = {0};
static struct hrtimer g_hrtimer_io_trigger_stop = {0};
static struct flymap_trigger_info g_io_trigger_info = {0};
static DECLARE_WAIT_QUEUE_HEAD(io_trigger_wait);
static int g_io_trigger_wait_flg = 0;

/**
 * rtk_trigger - rtk gpio trigger.
 * @on_off : RTK_TRIGGER_ON or RTK_TRIGGER_OFF
 */
static void rtk_trigger(int on_off)
{
    // printk("------------------------------trigger value : %d\n", on_off);
    gpio_set_value(FLYMAP_RTK_TRIGGER_GPIO, on_off);
}

/**
 * @brief gpio_11_5 触发fsync
 * 
 * @param _u8Num 
 */
static void gpio_trig_fsync_cfg(uint8_t _u8Num) 
{
    uint8_t u8Base;

    u8Base = (uint8_t)REG_READ(reg_11_virtual_addr + GPIO_DIR_ADDR);
    REG_WRITE(reg_11_virtual_addr + GPIO_DIR_ADDR,u8Base & (0xFF & ~(0x01 << _u8Num)));/*dir设置管脚5_1:输入*/

    u8Base = (uint8_t)REG_READ(reg_11_virtual_addr + GPIO_IS_ADDR);
    REG_WRITE(reg_11_virtual_addr + GPIO_IS_ADDR,u8Base & (0xFF & ~(0x01 << _u8Num)));/*is边沿触发中断*/

    u8Base = (uint8_t)REG_READ(reg_11_virtual_addr + GPIO_IEV_ADDR);
    //REG_WRITE(reg_11_virtual_addr + GPIO_IEV_ADDR,u8Base | (0x01 << _u8Num));/*iev上升沿或高电平触发*/
    REG_WRITE(reg_11_virtual_addr + GPIO_IEV_ADDR,u8Base & (0xFF & ~(0x01 << _u8Num)));/*iev下降沿或低电平触发中断*/

    REG_WRITE(reg_11_virtual_addr + GPIO_IC_ADDR, (0x01 << _u8Num));/*ic清除中断*/

    u8Base = (uint8_t)REG_READ(reg_11_virtual_addr + GPIO_IE_ADDR);
    REG_WRITE(reg_11_virtual_addr + GPIO_IE_ADDR,u8Base | (0x01 << _u8Num));/*ie启用中断*/

    printk("gpio config ok[%x]!\n",(0xFF & ~(0x01 << _u8Num)));
}

/*
*gpio地址映射
*/
static int virtual_addr_map(void) 
{
    reg_11_virtual_addr = (uintptr_t)ioremap_cache(GPIO11_REG_ADDR,0x1000);
    if(!reg_11_virtual_addr) {
        printk("%x ioremap addr failed !\n",GPIO11_REG_ADDR);
        return -1;
    }

    return 0;
}

/*取消地址映射*/   
static void virtual_addr_unmap(void) 
{
    iounmap((void*)reg_11_virtual_addr);
}

static int gpio_trig_irq_open(struct inode *inode,struct file *file)
{
    return 0;
}

static int gpio_trig_irq_close(struct inode *inode,struct file *file)
{   
    return 0;
}

/*
*read调用的具体函数，由它读取键盘输入的结果，
*实质上就是读取key_values数组的值
*完成键盘输入设备的核心功能，根据标志位ev_press判断是否可读
*如果可读，则读取数据到用户buffer中，如果不可读，
*则进程进入等待队列等待，直到数组可读为止
*等待队列机制，所中断管理中常用的机制。
*/
static ssize_t gpio_trig_irq_read(struct file *filp,
                            char __user *buff,
                            size_t count,loff_t *offp)
{
    return 0;
}    

static irqreturn_t irq_interrupt(int irq,void *dev_id)
{           
    int32_t exptime;
    int32_t totalTime;
    int32_t activeTime;
    unsigned long timerdelayus = 0;
    ktime_t tmstamp = ktime_get();

    REG_WRITE(reg_11_virtual_addr + GPIO_IC_ADDR,0x02 );    /* ic清除中断 */

    if (start_flag == 1) {
        start_flag = 0;
        exptime = g_io_trigger_info.exp_timestamp;
        totalTime = g_io_trigger_info.v_total_timestamp;
        activeTime = g_io_trigger_info.v_active_timestamp;
        // printk("io trigger event: exptime %d us, totalTime %d us, activeTime %d us\n", exptime, totalTime, activeTime);

        /* calc tirgger time */
        timerdelayus = ((totalTime + activeTime - exptime) + (totalTime - exptime)) / 2;
        // printk("timerdelayus = %ld\n", timerdelayus);

        /* start timer Pull up */
        hrtimer_start(&g_hrtimer_io_trigger, ktime_set(0, timerdelayus*1000), HRTIMER_MODE_REL);
        /* start timer drop-down */
        hrtimer_start(&g_hrtimer_io_trigger_stop, ktime_set(0, timerdelayus*1000+20000), HRTIMER_MODE_REL);

        g_io_trigger_info.trigger_timestamp = tmstamp / 1000 + timerdelayus;
        g_io_trigger_info.frame_timestamp = tmstamp / 1000 + totalTime;

        g_io_trigger_info.exp_timestamp = exptime;
        g_io_trigger_info.frame_no = 0;
        g_io_trigger_info.v_active_timestamp = activeTime;
        g_io_trigger_info.v_total_timestamp = totalTime;

        g_io_trigger_wait_flg = 1;
        wake_up_interruptible(&io_trigger_wait);
    }    

    return IRQ_HANDLED;
}

static long flymap_ioctl(struct file * pfile, unsigned int cmd,  unsigned long arg)
{
	unsigned long __user * argp = (unsigned long *) arg;
    struct flymap_trigger_info triggerInfo = {0};
    ktime_t tmstamp;

	switch(cmd) {
        case FLYMAP_TRIGGER_IO_EVENT:
            if (copy_from_user(&triggerInfo, (unsigned char *)argp, sizeof(struct flymap_trigger_info))) {
                printk("flymap_ioctl copy_from_user error...\n");
                return -EINVAL;
            }
            start_flag = 1;

			tmstamp = ktime_get();
			g_io_trigger_wait_flg = 0;
			triggerInfo.trigger_timestamp = tmstamp / 1000;
			g_io_trigger_info = triggerInfo;

			if (wait_event_interruptible_timeout(io_trigger_wait, g_io_trigger_wait_flg != 0, msecs_to_jiffies(1000/24*2)) <= 0) {
				printk("trigger timeout: trigger_timestamp %lld us\n", triggerInfo.trigger_timestamp);
				return -ERESTARTSYS;
			}           

			triggerInfo.trigger_timestamp = g_io_trigger_info.trigger_timestamp;
			triggerInfo.frame_timestamp = g_io_trigger_info.frame_timestamp;
			triggerInfo.exp_timestamp = g_io_trigger_info.exp_timestamp;
			triggerInfo.frame_no = g_io_trigger_info.frame_no;
			triggerInfo.v_active_timestamp = g_io_trigger_info.v_active_timestamp;
			triggerInfo.v_total_timestamp = g_io_trigger_info.v_total_timestamp;

            if (copy_to_user((unsigned char *)argp, &g_io_trigger_info, sizeof(struct flymap_trigger_info))) {
                printk("copy_to_user error...\n");
				return -1;
            }

            break;

		default :
			printk("Kernel: Inappropriate ioctl for cmd.\n");
			return -1;
	}

    return 0;
}

static struct file_operations dev_fops = 
{
    .owner = THIS_MODULE,
    .open      = gpio_trig_irq_open,
    .release = gpio_trig_irq_close,
    .unlocked_ioctl   = flymap_ioctl,
    .read = gpio_trig_irq_read,
    //.poll = gpio_trig_irq_poll,       /* 用户程序使用select调用的时候才会用到poll */
};

/*
*misc混合设备注册和注销
*/
static struct miscdevice misc = 
{
    .minor = MISC_DYNAMIC_MINOR,    /* 次设备号 */
    .name = DEVICE_NAME,            /* 设备名 */
    .fops = &dev_fops,              /* 设备文件操作结构体 */
};

static enum hrtimer_restart timer_isr_io_trigger(struct hrtimer *timer) 
{
	rtk_trigger(RTK_TRIGGER_ON);
	return HRTIMER_NORESTART;
}

static enum hrtimer_restart timer_isr_io_trigger_stop(struct hrtimer *timer) 
{
	rtk_trigger(RTK_TRIGGER_OFF);
	return HRTIMER_NORESTART;
}

static int __init flymap_vsync_init(void)
{
    int ret;

    ret = misc_register(&misc);
    if(0 != ret) {
        printk("register device failed！ !\n");
        return -1;
    }
    printk("register device success !\n");   

    ret = virtual_addr_map();       /* 地址映射 */
    if(0 != ret) {
        printk("virtual_addr_map failed！ !\n");
        return -1;
    }
    gpio_trig_fsync_cfg(GPIO_11_5); /* 管脚配置，要先进行地址映射 */

    /*注册中断*/
    ret = request_irq(gpio_to_irq(GPIO11_NUM(GPIO_11_5)), irq_interrupt, IRQF_TRIGGER_RISING, "TRIG", NULL);
    if(ret) {                       /* 如果注册中断失败，则释放已经成功注册的中断 */
        printk("request_irq failed！ !\n");
        return -1;
    }
#if 1
    /* rtk tigger gpio init */
    ret = gpio_request(FLYMAP_RTK_TRIGGER_GPIO, FLYMAP_RTK_TRIGGER_GPIO_LABER);
    if (ret) {
        printk("%s: failed to request GPIO r: %d\n", __func__, ret);
        return -1;
    }
    gpio_direction_output(FLYMAP_RTK_TRIGGER_GPIO, 1);

    /* hrt */
    hrtimer_init(&g_hrtimer_io_trigger, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	g_hrtimer_io_trigger.function = timer_isr_io_trigger;
	hrtimer_cancel(&g_hrtimer_io_trigger);

    hrtimer_init(&g_hrtimer_io_trigger_stop, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	g_hrtimer_io_trigger_stop.function = timer_isr_io_trigger_stop;
	hrtimer_cancel(&g_hrtimer_io_trigger_stop);

#endif
    return 0;   
}

static void __exit flymap_vsync_exit(void)
{
    misc_deregister(&misc);
    printk("unregister device success !\n"); 

    free_irq(gpio_to_irq(GPIO11_NUM(GPIO_11_5)),NULL);
    virtual_addr_unmap();   /* 取消地址映射 */

    gpio_set_value(FLYMAP_RTK_TRIGGER_GPIO, 0);
    gpio_free(FLYMAP_RTK_TRIGGER_GPIO);
}
 
module_init(flymap_vsync_init);
module_exit(flymap_vsync_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Xag");

